#!/usr/bin/env python
# -*- coding: utf-8 -*-

NAME=u'拱猪'
CONF=dict(
    #玩家-牌组数对
    player_cards=[[4, 1], [4, 2]], 
    #基牌总数
    cardBases=[2], 
)

HELP=u'''　　　　　　　　　　　　　　　拱　　猪

游戏简介：

　　拱猪是国内极为普遍的一种扑克牌游戏，目前附带在Windows中而流行的“红心大战(Hearts)”扑克牌游戏，与中国拱猪游戏的玩法相似，但并不完全相同，其规则性与娱乐性远不如拱猪游戏丰富。

详细规则、胜负判定方法：

　　在拱猪游戏中，应尽量避免拿到红桃牌(每张扣1分)和黑桃Q(每张扣10分)，因为这些牌在游戏中算分的，应尽量拿到方块J(每张加5分)，而拱猪是以得分最多方为赢家。梅花10为“变压器”，拿到的玩家得分翻倍。

　　发牌：拱猪游戏中没有大小王牌，一共52张，由计算机自动分配。

　　牌的大小按下列顺序排列：A、K、Q、J、10、9、8、7、6、5、4、3、2。其中黑桃Q代表“猪”，方块J代表“羊”。

出牌：

　　每轮只允许出一张牌；

　　第一局游戏由得到梅花2的一方首先出牌，并且必须出梅花2；

　　如果用户有首家出牌的花色时，必须先出该花色牌； 一轮结束后，最大方得到该轮的所有分牌。

亮牌：

　　出牌前先亮牌，可亮红桃A、黑桃Q、梅花10、方块J，亮后红桃、猪、变压器、羊各自的分值作用加倍。

基本打法：

　　有牌必跟：出牌必须与首家出牌的花色一致，如果没有该花色，就可以把红桃和黑桃Q垫出去，让此轮中出牌最大者得分。

'''

import general
from server import *
from client import *

#基数牌，游戏开始必须出的牌
baseCards=[Card(CLUB, TWO)]
#特殊牌：猪、羊、变压器
PIG=Card(SPADE, QUEEN)
SHEEP=Card(DIAMOND, JACK)
TRANS=Card(CLUB, TEN)
#所有红心
HEARTS=[Card(HEART, f).id for f in FACE]
#有分值的牌
SCORECARDS=[PIG.id, SHEEP.id, TRANS.id]+HEARTS
#可亮的牌
HEARTACE=Card(HEART, ACE)
SHOWCARDS=[PIG.id, SHEEP.id, TRANS.id, HEARTACE.id]

class Game(general.Game):
    '''游戏类，拱猪'''
    def __init__(self):
        super(Game, self).__init__()
        
        #游戏模块名称
        self.moduleName='GongZhu'
        #游戏名称
        self.name=NAME
    
    def setCardOrder(self, base=2):
        '''设置排序'''
        order=range(13)
        for i in range(base-1):
            c=order.pop()
            order.insert(0, c)
        order1=[i+100 for i in order]
        order2=[i+200 for i in order]
        order3=[i+300 for i in order]
        order=order+order1+order2+order3
        self.cardOrder=order
    
    def configure(self):
        '''追加设置'''
        super(Game, self).configure()
        #设置基数牌
        bcIndex=CONF['cardBases'].index(self.cardBase)
        self.baseCard=baseCards[bcIndex]
        #设置牌序
        self.setCardOrder(self.cardBase)
        #设置牌堆
        self.table.setPileCards(range(52)*self.cardPack)
        #设置积分盒
        self.table.fillScoreBox([0 for i in range(self.playerCount)])
            
        #亮牌计数
        self.showCount={}
        for k in SHOWCARDS:
            self.showCount[k]=0

    def collectCards(self):
        '''收集分值牌到收牌列表'''
        mid=self.markPlayerId
        cards=[data[0] for data in self.outList]
        collects=[]
        for card in cards:
            if card in SCORECARDS:
                collects.append(card)
        self.inList[mid]+=collects
        
    def calcScore(self):
        '''算分'''
        for i in range(self.playerCount):
            inCards=self.inList[i]
            score=0
            for card in inCards:
                if card in HEARTS:
                    score-=1*(self.showCount[HEARTACE.id]+1)
                elif card==PIG.id:
                    score-=10*(self.showCount[PIG.id]+1)
                elif card==SHEEP.id:
                    score+=5*(self.showCount[SHEEP.id]+1)
            if TRANS.id in inCards:
                score*=(2+self.showCount[TRANS.id])
            self.scoreBox[i]+=score

    def beginRound(self):
        '''一局游戏流程'''
        self.deal()
        #亮牌
        self.show()
        self.play()

    def show(self):
        '''亮牌'''
        self.notifyAll(SERVERID, SHOWMESSAGE, u'***亮牌***')
        #重置亮牌计数
        for k in SHOWCARDS:
            self.showCount[k]=0
        
        #首次亮牌id
        i=self.markPlayerId
        count=self.playerCount
        while count:
            self.tokenPlayerId=i
            self.notifyGameData()
            player=self.players[i]
            cmd_data=player.do(SHOWCARD)
            if not cmd_data:
                raise Exception('game error')
            cmd, cards=cmd_data
            r=self.judgeShow(player, cards)
            if r==UNDO:
                #亮牌无效，撤回
                player.do(UNDO, SHOWCARD)
            elif r==OK:
                for card in cards:
                    self.showCount[card]+=1
                self.outList[i]=cards
                self.notifyAll(player, SHOWCARD, cards)
                i+=1
                i%=self.playerCount
                count-=1
        
        time.sleep(3)
        self.clearOutList()
    
    def judgeShow(self, player, data):
        '''亮牌有效判定'''
        if not data:
            #不亮，允许
            return OK
        
        for card in data:
            if card not in SHOWCARDS:
                return UNDO
        return OK
        
    def play(self):
        '''打牌'''
        self.notifyAll(SERVERID, SHOWMESSAGE, u'***打牌***')
        #设置首轮最先出牌者
        if self.round==1:
            for player in self.players:
                if self.baseCard.id in player.cardList:
                    self.setMarkPlayerId(player)
                    break
        
        #首次出牌id
        i=self.markPlayerId
        #出牌计数（每轮）
        count=0
        while True:
            self.tokenPlayerId=i
            if count==self.playerCount:
                #每出牌一轮，大牌者收牌
                count=0
                self.collectCards()
                i=self.markPlayerId
                self.tokenPlayerId=i
                player=self.players[i]
                if not player.cardList:
                    #结束，算分
                    self.calcScore()
                    mscore=max(self.scoreBox)
                    winId=self.scoreBox.index(mscore)
                    self.notifyGameData()
                    self.notifyAll(self.players[winId], WIN)
                    break
                #游戏延时以便玩家看牌
                time.sleep(2)
                self.clearOutList()
            self.notifyGameData()
            player=self.players[i]
            cmd_data=player.do(PLAYCARD)
            if not cmd_data:
                raise Exception('game error')
            cmd, cards=cmd_data
            r=self.judge(player, cards)
            if r==UNDO:
                #出牌无效，撤回
                player.do(UNDO, PLAYCARD)
            elif r==OK:
                #出牌有效
                #填牌桌出牌列表
                self.outList[i]=cards
                #通知各玩家动作和更新游戏数据
                self.notifyAll(player, PLAYCARD, cards)
                count+=1
                i+=1
                i%=self.playerCount

    def judge(self, player, data):
        '''出牌有效判定'''
        if len(data)!=1:
            #每次出牌数只能1张
            return UNDO
        if self.markPlayerId==player.info['id']:
            #上次被标记的是自己，允许任意出，首轮有基数牌要先出
            cardid=data[0]
            if self.round==1 and (cardid!=self.baseCard.id) and (self.baseCard.id in player.cardList):
                return UNDO
            return OK

        #自己出的牌
        ocard=Card(data[0])
        #取得上次被标记玩家出的牌（大牌）
        mcards=self.outList[self.markPlayerId]
        mocard=Card(mcards[0])
        
        order=self.cardOrder
        if ocard.suit==mocard.suit:
            #同花色，允许
            if order[ocard.id]>order[mocard.id]:
                #更新大id
                self.setMarkPlayerId(player)
            return OK
        else:
            playersuits=[Card(cid).suit for cid in player.cardList]
            if mocard.suit in playersuits:
                #还有大牌花色，不允许出其他
                return UNDO
            return OK


class AI(general.AI):
    '''拱猪游戏的AI'''
    def __init__(self):
        super(AI, self).__init__()
        
    def initData(self):
        '''初始化游戏数据'''
        #获取基数牌
        cardBase=self.player.gameData['cardBase']
        bcIndex=CONF['cardBases'].index(cardBase)
        self.baseCard=baseCards[bcIndex]
    
    def showCard(self):
        '''实现亮牌逻辑'''
        cardList=self.player.cardList
        r=[]
        #把所有可亮的都亮
        for card in cardList:
            if card in SHOWCARDS:
                r.append(card)
        return r
    
    def playCard(self):
        '''实现出牌提示逻辑，这里是尽量出可出的牌'''
        markPlayerId=self.player.gameData['markPlayerId']
        cardList=self.player.cardList
        order=self.player.gameData['cardOrder']
        if markPlayerId==self.player.info['id']:
            if self.baseCard.id in cardList:
                #首牌有基数牌必出
                return [self.baseCard.id]
            
            #TODO:上次被标记的是自己（大），此次任意出牌
            return [random.choice(cardList)]
        
        #取得上次被标记玩家出的牌（大牌）
        outList=self.player.gameData['outList']
        mcards=outList[markPlayerId]
        mcard=mcards[0]
        mocard=Card(mcard)
        msuit=mocard.suit
        
        selfocards=[Card(cid) for cid in cardList]
        #牌按花色归类
        suitlist=[[] for i in SUIT]
        for c in selfocards:
            suitlist[c.suit].append(c)
        
        if suitlist[msuit]:
            #TODO:有大牌花色，可出
            #该花色的牌
            suitcards=[oc.id for oc in suitlist[msuit]]
            maxcard=max(suitcards, key=lambda x:order[x])
            mincard=min(suitcards, key=lambda x:order[x])
            #找小于大牌的最大牌
            lesscard=mincard
            for card in suitcards:
                if order[lesscard]<order[card]<order[mcard]:
                    lesscard=card
            #出的所有牌
            outcards=[cards[0] for cards in outList if cards]
            #出的所有花色
            outsuits=[Card(card).suit for card in outcards]
            
            if msuit==SPADE:
                #大牌为黑桃
                if PIG.id in cardList and  mcard>PIG.id:
                    #有猪且小，跑猪
                        return [PIG.id]
                else:
                    #否则尽量跑大
                    return [lesscard]
            
            if msuit==HEART:
                #大牌为红心，尽量跑大
                return [lesscard]
            
            if (PIG.id in outcards) or (HEART in outsuits):
                #出牌有猪或红心，尽量不要，跑大
                return [lesscard]
            
            if SHEEP.id in outcards:
                #有羊，尽量要，出最大
                return [maxcard]
                
            #TODO:否则任意出
            card=random.choice(suitcards)
            return [card]
        else:
            #TODO:垫牌
            if PIG.id in cardList:
                #有猪先垫
                return [PIG.id]
            if len(suitlist[HEART]):
                #优先垫红心
                return [suitlist[HEART][-1].id]
            #任意垫
            return [random.choice(cardList)]



